﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MoreLinq;

namespace ObviousCode.Stately.Compiler.Parser
{
    public class TransformCodeBlocks
    {
        TransformData _data;

        public TransformCodeBlocks(TransformData data)
        {
            _data = data;
        }

        public string StatelyInitialiseBlock
        {
            get
            {
                return "{0} _stately;".F(StatelyDataType);
            }
        }

        public string StatelyDataType
        {
            get
            {
                return "StateObserver<Step{0}{1}>"
                    .F(
                        _data.UseDataContext ? "," : "",
                        _data.UseDataContext ? _data.DataContextName : "");
            }
        }

        public string StatelyCtorHeaderBlock
        {
            get
            {
                StringBuilder builder = new StringBuilder();

                builder.AppendLine("_stately = new {0}(Step.{1});".F(StatelyDataType, _data.StartingState));
                builder.AppendLine();
                builder.AppendLine("\t\t\t_stately.ExceptionOccuredDuringTransition += (s, e) => { e.Data.Exception = e.Exception; _stately.Derail(Step.Exception, e.Data); };");
                builder.AppendLine();
                builder.AppendLine("\t\t\t_stately.AllowDatalessTransitions = {0};"
                    .F((!(_data.UseDataContext)).ToString().ToLowerInvariant()));

                return builder.ToString();
            }
        }


        public string StatelyAddTransitionBlocks
        {
            get
            {
                StringBuilder builder = new StringBuilder();
                string lastSource = null;
                _data
                    .SimpleTransitions
                    .Where(t => t.DestinationNodeType != Transition.NodeType.Wait)
                    .ForEach(t =>
                           {
                               if (t.SourceState.Data != lastSource) { builder.AppendLine(); }
                               
                               lastSource = t.SourceState.Data;
                               
                               builder.AppendLine("\t\t\t_stately.AddTransition(Step.{0}, Step.{1}, (t, d) => Handle{1}({2}));"
                                .F(t.SourceState.Data, t.DestinationState.Data, _data.UseDataContext ? "d" : ""));
                           });




                return builder.ToString();
            }
        }

        public string StatelyAddTransitionToBlocks
        {
            get
            {
                StringBuilder builder = new StringBuilder();

                _data
                    .WildcardTransitions
                        .ForEach(t =>
                            builder.AppendLine("\t\t\t_stately.AddTransitionTo(Step.{0}, (t, d) => Handle{0}({1}));"
                                .F(t.DestinationState.Data, _data.UseDataContext ? "d" : "")));

                if (_data
                        .Transitions
                        .Count(t => t.SourceNodeType == Transition.NodeType.WildCard &&
                                    t.DestinationState.Data == "Exception") == 0)
                {
                    builder.AppendLine("\t\t\t_stately.AddTransitionTo(Step.Exception, (t, d) => HandleException({0}));"
                            .F(_data.UseDataContext ? "d" : ""));
                }

                return builder.ToString();
            }
        }

        public string StatelyStartTransitions
        {
            get
            {
                StringBuilder builder = new StringBuilder();

                _data.Transitions
                    .Where(t => t.SourceState.IsStartState)
                    .ForEach(t =>
                        {
                            AddTransitionToBuilder(builder, t);
                        });

                return builder.ToString();
            }
        }

        public string StatelyTransitions
        {
            get
            {
                StringBuilder builder = new StringBuilder();

                GetWorkingTransitions()
                    .ForEach(t =>
                    {
                        builder.AppendLine("\t\tprivate ITransitionRequest Handle{0}({1})"
                            .F(t.DestinationState.Data, StatelyDataContextSignature));
                        builder.AppendLine("\t\t{");

                        _data
                            .SimpleTransitions
                            .Where(st => st.SourceState.Data == t.DestinationState.Data)
                            .ToList()
                            .ForEach(st =>
                                {
                                    AddTransitionToBuilder(builder, st);
                                });

                        if (t.DestinationState.IsCompletionState)
                        {
                            builder.AppendLine("\t\t\t//return CompleteRequest.Instance");
                        }

                        builder.AppendLine();
                        builder.AppendLine("\t\t\tthrow new NotImplementedException();");
                        builder.AppendLine();
                        builder.AppendLine("\t\t}");
                        builder.AppendLine();
                    });

                return builder.ToString();
            }
        }

        private List<Transition> GetWorkingTransitions()
        {
            List<Transition> workingTransitions =
                _data
                .Transitions
                .Where(st => st.DestinationState.Data != "Exception")
                .Where(st => st.DestinationNodeType != Transition.NodeType.Wait)
                .DistinctBy(st => st.DestinationState.Data)
                .ToList();
            return workingTransitions;
        }        

        private void AddTransitionToBuilder(StringBuilder builder, Transition st)
        {
            string signature = "(Step.{0}{1}{2});"
                .F(st.DestinationState.Data, _data.UseDataContext ? ", " : "", _data.UseDataContext ? _data.DataContextInstanceName : "");

            string transition = "";
            if (st.DestinationNodeType == Transition.NodeType.Wait)
            {
                builder.AppendLine("\t\t\t//return WaitRequest.Instance;");
                return;
            }
            else if (st.SourceState.IsStartState)
            {
                transition = "\t\t\t//_stately.Start{0}".F(signature);
            }
            else if (st.TransitionKind == Transition.Kind.Derail)
            {
                transition = "\r\n\t\t\t//_stately.Derail{0}\r\n\t\t\t//return HaltRequest.Instance;\r\n".F(signature);
            }
            else{

                transition = "\t\t\t//return _stately.T{0}".F(signature); 
                 
            }

            builder.AppendLine(transition);            
        }

        public string StatelyDataContextSignature
        {
            get
            {
                return "{0} {1}".F(
                    _data.UseDataContext ? _data.DataContextName : "",
                    _data.UseDataContext ? _data.DataContextInstanceName : ""
                    );
            }
        }

        public string StatelyCallbackDataType
        {
            get
            {
                return "Action{0}{1}{2}".F(
                    _data.UseDataContext ? "<" : "",
                    _data.UseDataContext ? _data.DataContextName : "",
                    _data.UseDataContext ? ">" : ""
                    );
            }
        }

        public string StepEnumeration
        {
            get
            {
                StringBuilder builder = new StringBuilder();

                builder.Append("public enum Step { ");

                if (_data.States.Count > 0)
                {
                    _data
                        .States
                        .ForEach(s => builder.Append("{0}, ".F(s)));

                    builder.Remove(builder.Length - 2, 2);

                    if (!_data.States.Contains("Exception"))
                    {
                        builder.Append(", Exception");
                    }
                }

                builder.Append(" }");

                return builder.ToString();
            }
        }
    }
}
